#include "Julianna.h"
#ifdef in_PC
#endif // in_PC
#include "TextBox.h"

//void text_print_from(JUI ui, Textbox box, V3b v3, int fill);
//void clean_line_from(JUI ui, int x, int y);
//int textBox_cursor_show();
//Textbox_t box1;
//JUI box1_uih;
TextboxView boxView;
JUI boxView_ui;
Li_List _textBoxView_uis = NULL; //List<JUI>, to save every instance ui handle
const string _textBoxView_labels[] = {"stdout","view1", "view2", "view3"};

int textBoxView_disp(JUI this_);
void textBoxView_deleteLines(TextboxView view, int lines);
JUI new_textBoxView(Gem gem, int cap);
void *textBoxView_pipe_line_out(void *arg, char *data, int n);
void delete_textBoxView(JUI view_ui);
int cmd_view(int n, char *arg[]);

void fun_clc()
{
	boxView->strFrom = 0;
	boxView->strEnd = 0;
	boxView->disp_from = 0;
	boxView->linesIndex->count = 0;
	juli_show_ui(boxView_ui, ByPass);
}

void textBoxView_setup()
{
	// static char sb[1024];
	Gem_t g = {10, 20, 460, 370};
	boxView_ui = new_textBoxView(&g, 512);
	if (boxView_ui == NULL)
		return;
	Pipe view_pipe_entry = pipe_find_entry(*_textBoxView_labels);
	lily_pipe_io_setup(view_pipe_entry); // bind the lily_out with screen out

	boxView = boxView_ui->id;
	public_a_fun_link_n("clc", fun_clc, 0);
	g.y += g.height + 2;
	// boxView_ui = new_textBoxView(&g, 512);
	int n = public_a_cmd_link("view", cmd_view);
	li_cmds[n].annotation = txbx_annotation;
}

/*-------------------for TextBoxView----------------*/

int textBoxView_skipLines(TextboxView view, int lines);
JUI new_textBoxView(Gem gem, int cap)
{
	TextboxView tx = malloc(sizeof(TextboxView_t));
	if (tx == NULL)
	{
		return NULL;
	}
	char *buf = malloc(cap);
	if (!buf)
	{
		free(tx);
		return NULL;
	}
	tx->linesIndex = new_list(sizeof(int), 4);
	if (tx->linesIndex == NULL)
	{
		free(tx);
		free(buf);
		return NULL;
	}
	tx->cap = cap;
	tx->disp_from = 0;
	tx->strEnd = 0;
	tx->strFrom = 0;
	tx->statues = 0;
	tx->s = buf;
	tx->last_max_x = -1;
	tx->last_max_y = -1;
	JUI bx_ui_handle = new_ui(gem, tx, textBoxView_onTouch, textBoxView_onShow);
	if (_textBoxView_uis == NULL)
	{
		_textBoxView_uis = new_li_cap(JUI, 1);
		if (!_textBoxView_uis)
		{
			delete_textBoxView(bx_ui_handle);
			return NULL;
		}
	}
	// public the pipe entry
	new_output_device(_textBoxView_labels[_textBoxView_uis->count], bx_ui_handle, textBoxView_pipe_line_out);

	li_add(_textBoxView_uis, bx_ui_handle);

	return bx_ui_handle;
}
// do view_ui = NULL after this
void delete_textBoxView(JUI view_ui)
{
	TextboxView view = view_ui->id;
	free(view->s);
	delete_list(view->linesIndex);
	free(view);
	remove_ui(view_ui);
	// remove the pipe
	if (_textBoxView_uis)
	{
		int index = list_find(_textBoxView_uis, &view_ui);
		if (index < 0)
			return;
		Pipe p = pipe_find_entry(_textBoxView_labels[index]);
		if (p)
			remove_a_pipe(p, 'e');
		list_remove_at(_textBoxView_uis, index);
	}
}
JUI textBoxView_onTouch(JUI this, int x, int y, TouchEvent mode)
{
	static int lastx = -1;//, x0 = 27;
	static int over_top = 0, selected = 0;
	static int t1 = 0;
	boxView = this->id;
	boxView_ui = this;
	if (mode == PressedDown)
	{
		lastx = y;
		//x0 = slidder_now;

		int t = lily_millis();

#ifdef in_PC
		if (t - t1 < 400)
#else
		if (t - t1 < 200)
#endif
		{
			selected = 1;
		}
		t1 = t;
		return NULL;
	}
	if (mode == PressedUp || mode == MoveOut)
	{
		lastx = -1;
		selected = 0;
		if (over_top)
		{
			if (over_top == 1)
			{
				Juli_fill(12, 1, this->width - 24, 4, BACK_COLOR);
				// Juli_fill(12, 1 - 15, this->width - 24, 4, BACK_COLOR);
			}
			else
			{
				Juli_fill(12, this->height - 4, this->width - 24, 4, BACK_COLOR);
				// Juli_fill(12, this->height + 15 - 4, this->width - 24, 4, BACK_COLOR);
			}
			over_top = 0;
		}
		return NULL;
	}

	if ((mode == Moving && lastx > 0) || (selected && mode == Pressed))
	{
		int dx = lastx - y;

		int dl = dx / fontSize;

		if (selected)
		{
			int yh = this->height / 2 - y;
			dl = yh / (this->height / 4);
		}

		if (dl)
		{
			if (textBoxView_skipLines(boxView, dl) >= 0)
			{
				lastx = y;
				return this;
			}
			else if (!over_top)
			{
				if (dl < 0)
				{
					over_top = 1;
					Juli_fill(12, 1, this->width - 24, 4, Color_red);
					// Juli_fill(12, 1 - 15, this->width - 24, 4, Color_red);
				}
				else
				{
					over_top = -1;
					// Juli_fill(12, this->height + 15 - 4, this->width - 24, 4, Color_red);
					Juli_fill(12, this->height - 4, this->width - 24, 4, Color_red);
				}
			}
		}
	}
	return NULL;
}
void textBoxView_onShow(JUI this, ViewEvent e)
{
	TextboxView view = this->id;
	if (e == OnCreate)
	{
		Juli_draw_rectangle(-1, -1, this->width + 2, this->height + 2);
		view->last_max_x = -1;
		view->last_max_y = -1;
		//Juli_draw_round_rect(-1, -15, this->width + 2, this->height + 30, 15);
	}
	// text_box_invalid(this);

	if (view->statues)
	{
		int n = view->linesIndex->count + 1;
		if (n - view->disp_from > this->height / fontSize)
		{
			view->disp_from = n - this->height / fontSize; // ((list_content(view->linesIndex, int))[n - 1 - this->height / fontSize] + 1) % view->cap;
		}
		view->statues = 0;
	}
	textBoxView_disp(this);
}
int textBoxView_disp(JUI this)
{
	TextboxView box = this->id;
	int last_max_x /*= -1*/, last_max_y /*= -1*/;
	last_max_x = box->last_max_x;
	last_max_y = box->last_max_y;

	int x = 0, y = 0;
	int wid = this->width;
	int hei = this->height;
	if (last_max_x >= 0)
	{
		wid = last_max_x;
	}
	if (last_max_y >= 0)
	{
		hei = last_max_y;
	}
	Juli_fill(0, 0, wid, hei, BACK_COLOR);
	// char*s = box->s+box->strFrom;
	int disp_from;
	if (box->disp_from > 0)
	{
		disp_from = (list_content(box->linesIndex, int))[box->disp_from - 1] + 1;
		disp_from %= box->cap;
	}
	else
	{
		disp_from = box->strFrom;
	}
	char *s = box->s + disp_from;
	char *se = box->s + box->strEnd;
	char *sc = box->s + box->cap;

	int half_fontSize = fontSize / 2;
	last_max_x = 0;
	last_max_y = 0;
	only_draw_text(1);
	while (s != se)
	{
		char c = *s;
		s++;
		if (s >= sc)
			s = box->s;
		if (c == '\a')
			continue;
		if (c == '\t') // to next tab, 0,4,8,...4n
		{
			int x4 = (x * 2 / fontSize / 4) * 4 + 4;
			x = x4 * fontSize / 2;
			continue;
		}
		else if (c == '\n')
		{
			y += fontSize;
			if (x > last_max_x)
				last_max_x = x;
			x = 0; // -fontSize / 2;
			continue;
		}

		if (x + half_fontSize >= this->width)
		{
			y += fontSize;
			x = 0;
			last_max_x = this->width;
		}
		if (y + fontSize >= this->height)
		{
			last_max_y = this->height;
			if (s != se) //not finished, but the box is too small
			{
				box->last_max_x = last_max_x;
				box->last_max_y = last_max_y;
				return 1;
				//textBoxView_skipLines(box, 1); //skip 1 line
			}
			break;
		}
		Juli_put_char(x, y, c);
		x += half_fontSize;
	}
	if (x > last_max_x)
		last_max_x = x;
	y += fontSize;
	if (y >= this->height)
		y = this->height;
	if (y > last_max_y)
		last_max_y = y;
	only_draw_text(0);
	box->last_max_x = last_max_x;
	box->last_max_y = last_max_y;
	return 0;
}
// 当内存不足时删除最旧行
// update disp_from
void textBoxView_deleteLines(TextboxView view, int lines)
{
	int i;
	if (lines < 1 || view->linesIndex->count < lines)
		return;
	int fromNew = ((list_content(view->linesIndex, int))[lines - 1] + 1) % (view->cap);

	for (i = 0; i < lines; i++)
	{
		list_remove_at(view->linesIndex, 0);
	}
	view->strFrom = fromNew;

	// check view.dispFrom, if the data has been deleted, change it then
	//int disp_from = list_content(view->linesIndex, int)[view->disp_from];
	int disp_from;
	if (view->disp_from > 0)
	{
		disp_from = list_content(view->linesIndex, int)[view->disp_from - 1] + 1;
		disp_from %= view->cap;
	}
	else
	{
		disp_from = view->strFrom;
	}
	//char* s = box->s + disp_from;
	char *sd = view->s + /*view->*/ disp_from;
	if (view->strFrom <= view->strEnd) // ...from --this-- end....
	{
		if (view->disp_from < view->strFrom || view->disp_from > view->strEnd)
			view->disp_from = 0; // view->strFrom;
	}
	else //  --this--end....from----
	{
		if (view->disp_from < view->strFrom && view->disp_from > view->strEnd)
			view->disp_from = 0; // view->strFrom;
	}
	return;

	char *s = view->s + view->strFrom;
	char *se = view->s + view->strEnd;
	char *sc = view->s + view->cap;
	while (s != se)
	{
		char c = *s;
		s++;
		if (s >= sc)
			s = view->s;
		if (c == '\n')
		{
			lines--;
			if (!lines)
			{
				view->strFrom = s - view->s;
				// check view.dispFrom, if the data has been deleted, change it then
//				char *sd = view->s + view->disp_from;
				if (view->strFrom <= view->strEnd) // ...from --this-- end....
				{
					if (view->disp_from < view->strFrom || view->disp_from > view->strEnd)
						view->disp_from = view->strFrom;
				}
				else //  --this--end....from----
				{
					if (view->disp_from < view->strFrom && view->disp_from > view->strEnd)
						view->disp_from = view->strFrom;
				}
				break;
			}
		}
	}
}
// 对于 disp_from 前后数行索引
//int lines_offset(TextboxView view, int lines, int from)
//{
//	if (lines == 0)
//		return -1;
//	char *s = view->s + from;
//
//	char *sc = view->s + view->cap;
//
//	if (lines > 0) //后向查找
//	{
//		char *se = view->s + view->strEnd;
//		while (s != se)
//		{
//			char c = *s;
//			s++;
//			if (s >= sc)
//				s = view->s;
//			if (c == '\n')
//			{
//				lines--;
//				if (!lines)
//				{
//					return s - view->s;
//				}
//			}
//		}
//	}
//	else //前向查找
//	{
//		char *se = view->s + view->strFrom;
//		lines--;
//		while (s != se)
//		{
//			char c = *s;
//			s--;
//			if (s <= view->s)
//				s = sc;
//			if (c == '\n')
//			{
//				lines++;
//				if (!lines)
//				{
//					s++;
//					if (s >= sc)
//						s = view->s;
//					return s - view->s;
//				}
//			}
//		}
//	}
//	return -1;
//}
// change disp_from to skip n lines
int textBoxView_skipLines(TextboxView view, int lines)
{
	int to = view->disp_from + lines;
	if (to < 0 || to > view->linesIndex->count)
		return -1;

	/*int at = lines_offset(view, lines, view->disp_from);
	if (at < 0)
		return at;*/
	view->disp_from = to;
	return 0;
}

void textBoxView_addString(TextboxView view, char *s)
{
	char *s_tail = view->s + view->strEnd;
	char *s_font = view->s + view->strFrom;
	char *sc = view->s + view->cap;
	//if (s_tail == s_font) //full
	//	return;

	while (*s != '\0')
	{
		*s_tail = *s;
		if (*s == '\n')
		{
			int at = s_tail - view->s;
			li_add(view->linesIndex, at);
		}
		s++;
		s_tail++;
		if (s_tail >= sc)
			s_tail = view->s;
		if (s_tail == s_font) //full
		{
			textBoxView_deleteLines(view, 1);
			s_font = view->s + view->strFrom;

			if (s_tail == s_font) // didn't delete any lines
			{
				view->strEnd = s_tail - view->s;
				return;
			}
		}
	}
	view->strEnd = s_tail - view->s;
	view->statues = 1;
}

void textBoxView_addChar(TextboxView view, char c)
{
	if (c == '\0')
		return;
	char *s_tail = view->s + view->strEnd;
	char *s_font = view->s + view->strFrom;
	char *sc = view->s + view->cap;
	//if (s_tail == s_font) //full
	//	return;
	*s_tail = c;
	s_tail++;
	if (s_tail >= sc)
		s_tail = view->s;
	if (s_tail == s_font) //full
	{
		textBoxView_deleteLines(view, 1);
	}
	if (c == '\n')
		li_add(view->linesIndex, view->strEnd);
	view->strEnd = s_tail - view->s;
	view->statues = 1;
}

// Pipe Entry, the arg is the handle of ui
// return ui handle so it can be updated
void *textBoxView_pipe_line_out(void *arg, char *data, int n)
{
	JUI view_ui = (JUI)arg;
	TextboxView view = view_ui->id;
	int i;
	for (i = 0; i < n; i++)
	{
		textBoxView_addChar(view, data[i]);
	}
	juli_show_ui(view_ui, ByPass);
	return view_ui;
}

const char *txbx_annotation = "text view tool:\n"
							  " new [-noExpend] [posRCN] [buffSize]\n"
							  " close\n";
int cmd_view(int n, char *arg[])
{
	if (n == 1 && boxView) // -info
	{
		sprintf(tx, "buff size:%d\n", boxView->cap);
		lily_out(tx);
		return 0;
	}

	if (*(arg[1]) == 'n') // new -noExpend 211
	{
		if (_textBoxView_uis->count >= 4)
		{
			li_error("too many views", -6);
		}
		int now = 2;
		Gem_t g = {boxView_ui->x, boxView_ui->y, boxView_ui->width, boxView_ui->height}; //{10, 32 + 100, 460, 312 + 25 - 100};

		if (!boxView_ui->hide)
		{
			if (n == 2) // new
			{
				window_expand(boxView_ui, 3, 3);
				window_split(&(boxView_ui->gem), "122", &g, 2);
			}
			else if (n > 2)
			{
				if (isD(*(arg[2]))) // new 211
				{
					window_expand(boxView_ui, 3, 3);
					if (window_split(&(boxView_ui->gem), arg[2], &g, 2) < 0)
					{
						li_error("bad arg", __LINE__);
					}
					now++; //3
				}
				else // new -noExpand, new -noExpand 212
				{
					now++; // 3
					if (n > 3)
					{
						if (window_split(&(boxView_ui->gem), arg[3], &g, 2) < 0)
						{
							li_error("bad arg", __LINE__);
						}
						now++; //4
					}
					else
						window_split(&(boxView_ui->gem), "122", &g, 2);
				}
			}
		}
		// after the textboxView
		int bfs = 256;
		if (n > now)
		{
			assert_msg(str_is_numeric(arg[now]), "bad arg, buff size is illegal");
			bfs = atoi(arg[now]);
		}
		boxView_ui = new_textBoxView(&g, bfs);
		if (boxView_ui == NULL)
		{
			li_error("start failed, may no space", -__LINE__);
		}
		boxView = boxView_ui->id;
		juli_show_up();
		return 0;
	}
	if (*(arg[1]) == 'h') // help
	{
		lily_out(txbx_annotation);
		return 0;
	}
	if (*(arg[1]) == 'c') // help
	{
		if (_textBoxView_uis->count < 2)
		{
			li_error("must keep 1 view", -3);
		}
		if (boxView_ui == *(list_content(_textBoxView_uis, JUI)))
		{
			lily_out("can't close stdout");
			return 0;
		}
		delete_textBoxView(boxView_ui);
		boxView_ui = list_content(_textBoxView_uis, JUI)[_textBoxView_uis->count - 1];
		boxView = boxView_ui->id;
		juli_show_up();
		return 0;
	}

	li_error("not support mode", -8);
}
